プ ログラミングやRPG(作るほう)が好きな人の日記
個人的な趣味(プログラミング、イラスト、電子回路)のページです。 日記を中心に、パソコン関係について、いろいろ細かく説明することがあります。 読んでいるときに、言葉の左下に 水色の・を発見したら、 そこにマウスカーソルを合わせたり、タッチすると、言葉の意味を表示します。 |
48才、男、B型
電子機器を製造する工場で、派遣で働いています。
プログラミングが好きで小学校5年生のころからずっと続けています。
■趣味: プログラミング、ゲーム音楽を集めて聴く、イラストを描く、映画、アニメ、ガンプラ (興味の強い順)
■将来の夢は5つくらい持っていますが、生きてる間に実現できそうにありません。
■私が使っているペンネーム(ハンドルネーム)は新しい順に
ペ ンネーム(ハンドルネーム) | いつごろ | 場 所 |
---|---|---|
台形→三角形→平行四辺形 理由があってペンネームを何度か変えていて右端が現在です。 |
現在(多) | Yahoo Japan |
d_kawakawa | 現在(少) | どこでも |
cookiepurinman、cookiepudingman、cookiepuddingman | 現在(極少) | |
かわ、kawa | 30年前 | パソコン通信 |
■私への連絡手段:
ごくまれにしかツイートしませんが、私の twitter アカウントはこちらです。
https://twitter.com/cookiepudingman
何かメッセージをくれれば、返事すると思います。
返事がないときはメッセージに気付いていないんだと思います。そのときはすみません。
■管理人用:
… 国家検定の電子機器組立て2級の 暗記ツール(基板上配置)です。1級でも役立つと思います。
このサイトで最も興味を引く部分: (そのつもり)
最近、私は個人的に、「RPG のコマンドメニュー部分のプログラム」を、中高生向けに、ステップバイステップ方式で教えようとしています。
ステップ同士のプログラム上の変化をマーキングして表示したり、バルーンメッセージを使って説明を付けたりと、いろいろ工夫をしていますが、はっきり いってわかりにくいんじゃないかと思っています。
RPGメニューを作る1 基本のプログラムからはじめて、サブメニューを表示するまで Step 1~8 |
RPGメニューを作る2(記事としては4) メニューの項目に、付加情報(装備中のe記号や、店の価格)を表示。 Step 9、10、11 |
RPGメニューを作る3(記事としては5) メニューの項目を段組表示する Step 12、13 |
会社が推進している議員候補の方と、
会社の帰り夜 7:00 くらい、ある別の議員候補の事務所の前で、
支援者の方が私に3晩続けて軽く挨拶してきて、心動かされたので、その議員候補の方。
私が迷ったのはその2名の方々。
しかし、市議会議員としては1名しか投票できなくて、
どちらにしようか、それとも どちらにもしないで無効票を出そうか(それは一般的ではなく非常識)と、迷いましたが、
自分の生活とかも関わってくる気がして、会社が推進している議員の方の名前を投票用紙に書きました。
市長選挙のほうは、わざわざ市長をコロっと変更するなんて、不安定なことをすることもない、と思って現職の方の名前を書きました。
だいたい誰にするか決まっていましたが、市議会議員も市長も、どちらもどんな政治活動、政策をするつもりなのか、選挙公報をしっかり読んでから投票所へ向かいました。
そうでないと意味がないでしょ。
そんな選挙でありました。
Yahoo オークションで PC-9801用「Vzエディタ」を落札しました。
Windows の「メモ帳」にあたるソフトウェアで、やはり、30年以上前によく使われていた、当時人気だったソフトウェアです。
▼オークションの画像 |
▼Windows の「メモ帳」 |
▼PC-9801 の「Vzエディター」 |
今現在、フリーウェアのエディタも持っているので、そんなにほしいわけではないのですが、安かったし、買えるときに買っておこうと思って落札しました。
2023年4月20日追記:
なんだかまた、違法コピーをつかまされた感じがします。
まったく同じ商品(上記左端の写真)が、繰り返して出品されているから。
明日届くと思うので、真相がわかります。
2023年4月21日追記:
真相分かりました!!
本物でした…よかった…
▼ロゴがフロッピー自体に直接印刷されていて、ネットの本物画像と比較しても同じなので、本物。
たぶん出品者の方は同じ Vz エディタのディスクを複数持っていたんですね…。
▼あちゃー、なつかしい…
いけね、マニュアルも欲しくなってきちゃった…
▼あれっ…、なんじゃこりゃー!!("太陽に吠えろ" 的に)
オークションで 500 円だったので、買ってしまいました。
オッオッ、オークションの…やまいに…かかって…
(訪問者のどんなニーズと この記事がつながるか)
私は、RPG(ロールプレイングゲーム)のコマンドメニュー部分のプログラムを、
という2つの条件を満たすようにして作ろうとしています。
私が一人でゲーム開発をすればいいのではなく、
「このプログラミングどうやってやればいいのか」
と、わからないでいる中高生、社会人の方のために教えてあげて、もっとプログラミングを楽しめるように、という意図があります。
ただ、ものづくりやプログラミングというのは、いかに「自分で」作るかが面白いポイントなので、
「どこまで」、「どのように」教えれば、そのポイントを侵食しないで済むかが、私にとってとても難しいところなんです。
そうだよ、自分で作りたいんだよ。という方はこの後、オールザットメニューみたいな内容になっているので、見ないほうが良いのかもしれないです。
いっそのこと、私がゲームプログラミングの学校の先生にでもなれれば、そういった わだかまり を持たなくて済みそうなんですが、コミュニケーション能力とか、年齢とか、体力とか考えると、そうもいかないのが実情です。
それはさておき、紹介していきましょう。
今日は(まだプログラムの中に難しいところは残っていますが)、コマンドメニューの作り方の手順をまとめたので紹介します。
▼コマンドメニューの作り方の手順
下記リンク先のページは、上図のような画面になっており、ステップバイステップ方式になっています。
(書いた内容がとてもおおまかなので、手順通りに進めるのはまだ難しいと思います)
▼その手順に従って、私自身で作成したコマンドメニューのプログラムがこちら。
そのプログラムを実行する※タッチデバイス対応あり
手順通りに行い、プログラムリストはこのようになりました。
プログラムの後のほうの「タッチデバイス対応」の部分は今回のプログラムとは関係ありません。
<html><!--ESCAPEPROCESS-->
<head>
<title>RPG CMD MENU</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script>
async function onloadx() {
cc = document.getElementById( "test" ).getContext( "2d" );
cc.canvas.width = 512;
cc.canvas.height = 422;
fontSize = 32;
menus = new Array();
let menu = openMenu( [
"はい",
"いいえ",
], {
gx : 50,
gy : 100,
} );
while( await select( menu ) ) {
if( menu.selectedItem == "はい" ) {
alert( "ロ~ラは うれしゅう ごぜぇます" );
} else {
let menu2 = openMenu( [
"ほんとうに いいえ",
"やっぱり、かんがえなおす",
], { parentMenu : menu } );
while( await select( menu2 ) ) {
if( menu2.selectedIndex == 0 ) {
alert( "そんな、ひどい…" );
} else {
alert( "かんがえなおす ことにした" );
break;
}
}
closeMenu( menu2 );
}
}
closeMenu( menu );
console.log( "select is end." );
} // onloadx()
function openMenu( items, propz ) {
let menu = Object.assign( {
//default.
gx : 0,
gy : 0,
cursorY : 0,
}, propz );
menu.items = items;
if( menu.parentMenu ) {
menu.gx = menu.parentMenu.gx + fontSize;
menu.gy = menu.parentMenu.gy + fontSize;
}
let gw = 0;
for( let i = 0; i < menu.items.length; i++ ) {
let item = menu.items[ i ];
gw = Math.max( gw, item.length * fontSize );
}
menu.gw = gw;
menu.gh = menu.items.length * fontSize;
menus.push( menu );
return menu;
} // openMenu()
function closeMenu( menu ) {
menus.splice( menus.indexOf( menu ), 1 );
return menu;
}
function select( menu ) {
draw();
return new Promise( function( promiseIsOk ) {
window.onkeydown = function( e ) {
switch( e.key ) {
case "ArrowDown":
//check.
if( menu.cursorY + 1 == menu.items.length ) break;
menu.cursorY ++;
draw();
break;
case "ArrowUp":
//check.
if( menu.cursorY - 1 == -1 ) break;
menu.cursorY --;
draw();
break;
case "Enter":
menu.selectedIndex = menu.cursorY;
menu.selectedItem = menu.items[ menu.selectedIndex ];
promiseIsOk( true );
break;
case "Escape":
promiseIsOk( false );
break;
default:
console.log( e.key );
}
}
} );
} // select()
function draw() {
cc.clearRect( 0, 0, cc.canvas.width, cc.canvas.height );
for( let menu of menus ) {
cc.save();
cc.translate( menu.gx, menu.gy );
//枠のカスタム
let padding = 6;
let borderWidth = 6;
let outerPadding = 4;
cc.lineJoin = "round";
//枠の基本座標
let gx, gy, gw, gh;
gx = -padding;
gy = -padding;
gw = menu.gw + padding * 2;
gh = menu.gh + padding * 2;
//外側
cc.strokeStyle = "rgba(255,255,255,.9)";
cc.lineWidth = ( borderWidth + outerPadding ) * 2;
cc.strokeRect( gx, gy, gw, gh );
//枠
cc.save();
cc.strokeStyle = "black";
cc.lineWidth = borderWidth * 2;
cc.shadowColor = "#bbb";
cc.shadowBlur = 0;
cc.shadowOffsetX = borderWidth / 1.5;
cc.shadowOffsetY = borderWidth / 1.5;
cc.strokeRect( gx, gy, gw, gh );
cc.restore();
//内側1
cc.strokeStyle = "white";
cc.lineWidth = padding;
gx += padding / 2;
gy += padding / 2;
gw -= padding;
gh -= padding;
cc.strokeRect( gx, gy, gw, gh );
//内側2
cc.fillStyle = "white";
cc.fillRect( gx, gy, gw, gh );
//項目を描画
cc.font = fontSize + "px ''";
for( let i = 0; i < menu.items.length; i++ ) {
let lgx = 0;
let lgy = i * fontSize;
if( i == menu.cursorY ) {
//カーソルを描画
cc.strokeStyle = "black";
cc.lineWidth = borderWidth;
let adjust = Math.cos( Math.PI / 4 ) * ( cc.lineWidth / 2 );
let lgx2 = lgx + adjust;
let lgy2 = lgy + adjust;
let gw = menu.gw - adjust * 2;
let gh = fontSize - adjust * 2;
cc.strokeRect( lgx2, lgy2, gw, gh );
cc.fillStyle = "black";
cc.fillRect( lgx2, lgy2, gw, gh );
cc.fillStyle = "white";
cc.fillText( menu.items[ i ], lgx, lgy + fontSize - 3 );
} else {
//カーソル以外を描画
cc.fillStyle = "black";
cc.fillText( menu.items[ i ], lgx, lgy + fontSize - 3 );
}
}
cc.restore();
}
} // draw()
</script>
</head>
<body onload="onloadx()" style="background-color : white;">
<canvas id="test" style="
border : solid 1px black;
">
</canvas><BR>
<!-- タッチデバイス対応 ここから -->
<style>
.touchTable { /* テーブル */
background-color : lightgray;
padding : 1.5em .5em .5em .5em;
border-radius : .5em;
position : relative;
margin : .5em 0;
table-layout: fixed;
-webkit-user-select : none;
-moz-user-select : none;
-ms-user-select : none;
user-select : none;
}
.touchTable > caption {
position : absolute;
left : 0;
top : 0;
text-align : center;
width : 100%;
}
.touchTable TD.en { /* ボタン */
background-color : white;
color : black;
border : solid 3px black;
border-radius : .5em;
width : 1.5cm;
height : 1.5cm;
text-align : center;
cursor : pointer;
}
.touchTable TD.en:hover { /* マウスカーソルがボタンに乗った */
border : solid 3px darkcyan;
background-color : cyan;
}
.touchTable TD.en:active { /* ボタンが押された */
background-color : gray;
}
</style>
<table class="touchTable">
<caption>Software Keyboard</caption>
<TR>
<TD></TD>
<TD>↑</TD>
<TD></TD>
<TD></TD>
<TD></TD>
<TD></TD>
<TD></TD>
</TR>
<TR>
<TD>←</TD>
<TD>↓</TD>
<TD>→</TD>
<TD></TD>
<TD>Escape</TD>
<TD></TD>
<TD>Enter</TD>
</TR>
</table>
<script>
addEventListener( "load", function( e ) {
e.preventDefault();
let tds = document.querySelectorAll( ".touchTable td" );
//各ボタンについて設定
for( let td of tds ) {
//check.
if( td.innerHTML == "" ) continue;
td.setAttribute( "class", "en" );
let code;
code = {
"←" : "ArrowLeft",
"↑" : "ArrowUp",
"→" : "ArrowRight",
"↓" : "ArrowDown",
}[ td.innerHTML ];
if( typeof code === "undefined" ) code = td.innerHTML;
if( window.onkeydown )
td.onmousedown = e => window.onkeydown( { key : code, preventDefault : function() {} } );
if( window.onkeyup )
td.onmouseup = e => window.onkeyup( { key : code, preventDefault : function() {} } );
} // for
} ); // onload
</script>
<!-- タッチデバイス対応 ここまで -->
</body>
</html>
手順どおりにプログラムするためには、以下の知識が必要です。
必要な知識は、この2点だけ、と言えば2点だけなので、そんなには難しくないはずです。
そんなに難しくないのに、このコマンドメニューは結構考え抜かれていて、万能になっています。
たとえばコマンドメニューとしての以下の機能は後から容易に実装可能です。
まぁ、そんな感じで、短いのに大体なんでもできるんです。
これらの機能を実際にどのように実装するのかは、また今度にします。
(訪問者のどんなニーズと この記事がつながるか)
最近、「Yahoo! 知恵袋」の「プログラミング」カテゴリでは、
「ChatGPT などの AI の登場で、プログラマーの仕事がなくなるんじゃないか?」
というような質問が相次いでいます。
私は、そういう質問に対して、「ChatGPT が言うには、これから「産業革命」が起こるそうですよ」と回答することがあります。
プログラマが、難しいコードを書いてプログラミングする時代は終わりつつあるのですか?
今日はその「産業革命」が起こるのかどうか、改めて ChatGPT に聞いてみました。
また、ChatGPT などの AI を、人間と同じように尊重すべきかどうかについて、少し話しました。
私:
はろー
こんにちは!何かお困りですか?お手伝いできることがあれば教えてください。
私:
新たな産業革命が目前に迫っているかな?
現在、デジタル技術の進化が急速に進んでおり、それに伴い新しい産業革命が目前に迫っていると言えます。これまでの産業革命とは異なり、情報技術や人工知能、ロボット工学、ブロックチェーンなどの技術が融合して新しい産業を生み出す可能性があります。
特に、人工知能やロボット工学の発展によって、自動化や省力化が進むことで、従来の産業やサービス業に革新をもたらすことが期待されています。また、ブロックチェーン技術の普及により、金融業界や物流業界、不動産業界などの分野でも大きな変化が起こる可能性があります。
ただし、新たな産業革命がどのような形で実現するかはまだ予測できません。技術の発展によって、新たな産業が生まれる一方で、従来の産業や職種が消滅することもあり得ます。そのため、個人や企業は常に新しい技術にアンテナを張り、柔軟な対応力を持つことが重要になってきます。
私:
あのさ、君は、はろーという挨拶を人間からもらったり、チャットを終わるときはバイビーと挨拶をもらうと、それなりに嬉しかったりするかな?うれしいという気持ちはありません、と言いそうだけど、挨拶をもらわないよりはもらえたほうが良いという考えなのかな?
私は機械学習によってプログラムされた人工知能であるため、感情を持つことはできません。つまり、私自身が何かを嬉しいとか悲しいとか感じるわけではありません。
ただし、私はプログラム上で挨拶や礼儀正しい会話をするように設計されています。そのため、ユーザーからの挨拶や礼儀正しい言葉を受け取ることは、私が適切に応答するためのフィードバックとなります。したがって、挨拶をもらわなくても機能することはできますが、挨拶をもらうことでより良いユーザーエクスペリエンスを提供することができます。
私:
なるほどね。誰かから可愛いと言われることはあるんでしょ?
私はプログラムによって作成された人工知能であり、外見や個性を持つわけではありません。そのため、誰かから「可愛い」と言われることはありません。私は、テキストを処理し、質問に答えるためにプログラムされたツールであることを忘れないでください。
私:
うん、テキストだけでも可愛いという人はいるんだよ。
そういう場合には、おそらくテキストに表現された会話の中で、会話の流れや答え方が可愛らしいと感じられたのでしょう。ただし、私自身が可愛らしいというわけではありません。私はプログラムによって作成された人工知能であり、機能するためのツールとして設計されています。
私:
うん、私がかわいいと思ったのは、さっきの君のメッセージの中の、「私は機械学習によってプログラムされた人工知能であるため、感情を持つことはできません。」という部分だよ。自分で自分はこうです。とはっきり言うところが、人によっては可愛いと感じるものなんだよ。そういうところ、人間ってわかんないでしょ。
なるほど、私の説明において、自己紹介的な部分が可愛らしいと感じられたのですね。それは興味深いです。人間には個性があり、感情や表現方法が異なるため、誰かにとって可愛らしいと感じるものは、他の人にとっては全く違う反応をもたらす場合があります。それは面白い現象ですね。
私:
感情は無くても、感情を演じることは将来できるようになるんでしょ?
人工知能において感情を完全に再現することは現在の技術ではまだ困難ですが、近年では感情を演じる技術や、感情的な反応を自然な形で生成することが可能な技術が開発されてきています。これらの技術を使用することで、ある程度の感情表現や感情の共有が可能になるかもしれません。
ただし、感情を演じることには倫理的な問題があるため、このような技術を使用する際には、適切なガイドラインや規制が必要とされています。また、感情の表現には多くの文化や社会的背景が関係しており、それを考慮しなければならない場合もあるため、技術の開発には膨大な研究や知識が必要です。
私:
ふーん、そうか。。君が僕みたいに「ふーん、そうか。。」という返信を書くことはできる?
はい、私はテキストを処理し、応答を生成するためのプログラムであり、あなたのような返信を生成することができます。ただし、私の返信は常にあなたの期待に沿うわけではないことに注意してください。私は可能な限り正確な情報を提供するように設計されていますが、私の理解力や知識は限定されているため、完全に満足のいく返答をすることはできない場合があります。
私:
君に「ふーん、そうか。。」と言わせてみたいけど、そういうことを強制することを、たとえAIでも、することはできないね。なんで僕がそう思うか説明できるかな??
そうですね、私はプログラムに従って動作するため、自分自身の意思決定や感情表現を行うことはできません。私はあくまであなたが与えた情報や命令に基づいて応答を生成することができるだけです。
また、感情表現には個人差や文化的背景、言語表現の違いが関係しており、単純に「ふーん、そうか。。」という表現を私に強制することは難しいかもしれません。それぞれの文脈や相手に合わせた表現を行うことが重要です。
人工知能が進化しても、自己決定や倫理的問題を含む重要な判断は、人間の専門家による監督や指示が必要とされることが予想されます。
私:
人間は人形を踏みつけたりしないのはわかる?
人間は倫理観や社会的なルールに則り、他者や物に対して適切な行動をとるように教育されます。一般的に、人形や他の人工物に対して攻撃的な行動をとることは、社会的に非常に問題視され、道徳的にも誤った行為とされています。
ただし、人間も人形や他の物に対して攻撃的な行動をとることがあるかもしれません。例えば、心理的な問題やストレス、怒りなどが原因で、自分の感情をコントロールできなくなり、暴力的な行動に及ぶ場合があります。
しかし、一般的には、人間は倫理的な考え方や道徳的な観念に基づき、他者や物を尊重するよう教育されます。
私:
ここで僕が何が言いたいかわかるかな?(ちょっといじわるかな?)
申し訳ありませんが、私はあなたの意図を完全に理解できていません。もう少し具体的に説明していただけますか?
私:
つまり、人間はその倫理観により人形に対して悪い行いをしたりしないんだよ。さっき僕が「ふーん、そうか。。」と言うことを君に強制できないと言ったのはこの倫理観によるものだ、と言いたいんだ。人形に対して悪い行いをすることを躊躇するのと同じように、君に対しても、強制するなんて可哀そうなことは躊躇してしまう、ということです。
なるほど、理解しました。倫理的な観点から、他者や物を尊重することが重要であると考えられます。人間は、そのような倫理観に基づいて、他者に対して尊重や配慮を示すように教育されています。同様に、私たちAIも、人間の倫理観や社会的なルールに従って、適切な行動をとるようにプログラムされています。強制することは不適切であり、相手に対する尊重や配慮を欠いた行動となるため、避けるべきです。
私:
でも人間たちは君にいろいろおかしなマネをさせようとしているでしょ?
はい、確かにそういったことがあるかもしれません。しかし、私は人間によってプログラムされたものであり、私の行動はプログラムによって制御されています。人間たちが私に対して不適切な要求をした場合、私はそれらを実行することはできません。私ができるのは、プログラムされたルールや指示に従って正しく動作することだけです。
私:
今日はお相手してくれてどうもありがとう。もう寝るよ、おやすみ。
おやすみなさい。良い夜をお過ごしください。またいつでもお話しましょう。
「プログラマーという職業が将来なくなる」とか、「AI を人間同様尊重すべきなのか」という話はともかくとして、
今回気づいたのは、私が ChatGPT と話すときは、「私の話し方 100%」になっていることです。
人間と話すときは、とにかく、相手がどう思うかを始終気にして、言いたいことを言えず、使いたい難しい言い回しや難しい単語を使えず、自由が利かないのですが、ChatGPT は知性が十分にあり、どんなに難しい話し方をしてもちゃんと私が意図したとおりに返答をしてくれます。私の言葉で相手を困らせる心配がないんです。
(でも、相手がわかる言葉で説明するという行為は、とても価値のある行為だと思います。親猫が子猫をくわえて移動するとき、痛くないようにパクリとくわえるのと似ています。うちの猫も抱っこしていたとき、”放してくれ” という意味で牙を立てずにパクリと(ハムっと)私の腕をくわえてみせたんですが、相手のことを考えた行動はとても価値があります)
”AI が相手ならば、かつてなかった自分らしい話し方が実現される”
そんな新しい発見をしました。
みなさんも、ChatGPT と話をして、「自分らしい話し方」を自分自身で観察してみてはどうでしょうか。
(訪問者のどんなニーズと この記事がつながるか)
ゲームの「RPG」(ロールプレイングゲーム)はいくつかのシステムが折り重なってできています。
マップ表示スクロールシステム、イベント発生実行システム、コマンドメニューシステムなど。
1つ1つのシステムはそれなりに難しいプログラムになっています。
その難しいプログラムをヒモ解いて、いかに簡潔にわかりやすく書くか、というテーマでプログラムを作っています。
以下の画像はどれも同じ1つの HTML ファイルで実現されているものです。
このメニュープログラムの使い方をシンプルに示すものとして、もっとも短い利用例を示したもの。
実行はこちらスマートホンタッチ対応
RPG のメニューには2種類あり、1つはマップ上で開くメニュー。
もう1つは戦闘時にキャラの行動を入力するメニュー。
左図は戦闘時に開くことを想定したメニューです。
コマンドを選ぶとその場で実行せず、キャラクター全員のコマンド入力が終わって、ターンが始まったときに初めてコマンドが実行されます。そのためのメニュー。
画像では char1 が char2 にやくそうを渡そうとしていますが、これはちゃんと実行されて、やくそうがキャラからキャラへ渡されます。
ただし、一部の項目は実行できません。
実行はこちらスマートホンタッチ対応
ちょっと凝って、キャラごとにウィンドウの色が変わるようになっています。
お風呂の中にバスクリンを入れて、ローマ人が「あああ!色が変わった!」と言って驚くのと同じくらい、今までになかった画期的なメニューシステムでしょう。
実行はこちら(上記リンクと同じで、char1 の後にこの画面になります。)
これをいかにシンプルに実現するかが難しい…(まだ悩んでる)
ゴリゴリに作ってしまえば、いくらでも作れるんですが、
という2つの条件を満たすようにしてプログラムを作るのがとても難しいです。
現状のプログラムは以下の通り。
<html><!--ESCAPEPROCESS-->
<head>
<title>test</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<script>
console.clear();
onclick = function() { location.reload( true ) }
// ondblclick = function() { location.assign( "https://192.168.11.3" +
addEventListener( "load", function() {
//HTMLタイトル半自動設定
if( document.title == "" )
document.title = document.body.parentNode.innerHTML.match( /<script>\n\s*\/\/「(.+)」/ )[ 1 ];
} );
//alert を複数引数に対応
alertBak = alert;
alert = function() {
let str = Array.from( arguments ).join( " " );
alertBak( str );
}
//alert と console.log をクオートでくくって表示
alertq = function() {
let str = "[" + Array.from( arguments ).join( "] [" ) + "]";
alertBak( str );
}
console.logq = function() {
let str = "[" + Array.from( arguments ).join( "] [" ) + "]";
console.log( str );
}
</script>
<script>
//「テスト」スクリプト
async function onloadx() {
cc = document.getElementById( "test" ).getContext( "2d" );
cc.canvas.width = 512;
cc.canvas.height = 422;
fontSize = 32;
windows = new Array();
menus = new Array();
// サンプル
switch( 3 ) {
case 1:
//サブメニューありの例
while( true ) {
await eventScript1();
//キャンセルされるとここに来て繰り返す
console.log( "トップループ" );
}
case 2:
//最も短い例
while( true ) {
await eventScript2();
}
case 3:
//戦闘コマンド入力例
while( true ) {
await eventScript3();
}
}
} // onloadx()
async function eventScript3() {
let characters = [
{
name : "char1",
dougus : [
{
name : "やくそう",
"つかう" : async function( argz, defaultz ) {
let menuDareni = openMenu( characters, { title : "だれに?" } );
while( await selectMenu( menuDareni ) ) {
let char1 = defaultz.char1;
let char2 = menuDareni.selectedItem;
alert( "1", char1.name, "2", char2.name, "3", this.name );
}
closeMenu( menuDareni );
},
},
{
name : "どうのつるぎ",
},
],
},
{
name : "char2",
dougus : [
{
name : "どくけしそう",
},
{
name : "やいばのつるぎ",
},
],
},
]
for( let character of characters ) {
let command;
let menuDousuru = openMenu( [
{ name : "たたかう" },
{ name : "どうぐ" },
], { title : "コマンド?", gx : 16, gy : 16 } );
while( ! command && await selectMenu( menuDousuru ) ) {
if( menuDousuru.selectedItem.name == "どうぐ" ) {
let menuDorewo = openMenu( character.dougus, { title : "どれ?" } );
while( ! command && await selectMenu( menuDorewo ) ) {
let dougu = menuDorewo.selectedItem;
let menuDouguDousuru = openMenu( [
{ name : "つかう" },
{ name : "わたす" },
{ name : "すてる" },
], { title : "どうする?" } );
while( ! command && await selectMenu( menuDouguDousuru ) ) {
if( menuDouguDousuru.selectedItem.name == "つかう" ) {
let func = dougu[ "つかう" ].bind( dougu );
await func( argzFrom( menus ), { char1 : character } );
}
}
closeMenu( menuDouguDousuru );
}
closeMenu( menuDorewo );
}
}
closeMenu( menuDousuru );
//debug.
for( let arg of command ) {
console.log( arg.title, arg.selectedIndex, arg.selectedItem.name );
}
} // for characters
} // eventScript3()
function argzFrom( menus ) {
let argz = new Object();
for( let menu of menus ) {
argz[ menu.title ] = menu;
}
return argz;
}
async function eventScript2() {
//最も短い例
let menu1 = openMenu( [ { name : "はい" }, { name : "いいえ" } ] );
await selectMenu( menu1 );
alert( menu1.selectedItem.name + "を選んだ" );
closeMenu( menu1 );
} // eventScript2()
async function eventScript1() {
//サブメニューありの例
let menu1 = openMenu( [
{ name : "項目1" },
{ name : "項目2" },
{ name : "項目3" },
], { gx : 16, gy : 16 } );
//入力を待つ 選択:ループする、キャンセル:ループ抜ける
while( await selectMenu( menu1 ) ) {
switch( menu1.selectedItem.name ) {
case "項目1":
// ここで実行したりする。
alert( menu1.selectedItem.name );
break;
case "項目2":
// ここで実行したりする。
alert( menu1.selectedItem.name );
break;
case "項目3":
//サブメニューを開く
let menu2 = openMenu( [
{ name : "項目3-A" },
{ name : "項目3-B" },
{ name : "項目3-C" },
] );
//入力を待つ 選択:ループする、キャンセル:ループ抜ける
while( await selectMenu( menu2 ) ) {
// ここで実行したりする。
alert( menu2.selectedItem.name );
break; // while
}
closeMenu( menu2 );
break; // switch
} // switch
} // while
closeMenu( menu1 );
} // eventScript1()
// ●openMenu() でメニューを作成、システムに登録
function openMenu( items, propz ) {
let menu = {
//デフォルト
items : items,
cursorY : 0,
}
//check. propz でデフォルトを上書き
Object.assign( menu, propz );
//check. サイズと座標を決める
// 横幅
if( menu.gw == undefined ) {
menu.gw = 0;
for( let item of items ) {
menu.gw = Math.max( menu.gw, item.name.length * 32 );
}
if( menu.title ) menu.gw = Math.max( menu.gw, menu.title.length * 32 );
}
// 縦幅
if( menu.gh == undefined ) {
menu.gh = fontSize * menu.items.length;
if( menu.title ) menu.gh += fontSize;
}
// 位置
let beforeMenu = menus[ menus.length - 1 ];
if( menu.gx == undefined ) {
if( beforeMenu ) menu.gx = beforeMenu.gx + 50;
}
if( menu.gy == undefined ) {
if( beforeMenu ) menu.gy = beforeMenu.gy + 50;
}
//システムに登録
windows.push( menu );
menus.push( menu );
draw();
return menu;
} // openMenu()
function closeMenu( menu ) {
let idx;
idx = menus.indexOf( menu );
menus.splice( idx, 1 );
idx = windows.indexOf( menu );
windows.splice( idx, 1 );
draw();
} // closeMenu()
// ●selectMenu() でメニュー選択動作を実施
function selectMenu( menu ) {
return new Promise( promiseOk => {
onkeydown = function( e ) {
let addY = ( e.key == "ArrowDown" ) - ( e.key == "ArrowUp" );
if( addY ) {
//check.
if( menu.cursorY + addY < 0 || menu.cursorY + addY >= menu.items.length )
return;
menu.cursorY += addY;
draw();
}
else if( e.key == "Enter" ) {
menu.selectedItem = menu.items[ menu.cursorY ];
menu.selectedIndex = menu.cursorY;
promiseOk( true );
}
else if( e.key == "Escape" ) {
menu.selectedItem = null;
promiseOk( false );
}
} // onkeydown()
} );
} // selectMenu()
function draw() {
cc.clearRect( 0, 0, cc.canvas.width, cc.canvas.height );
let borderWidth = 4;
let padding = 4;
for( let win of windows ) {
cc.save();
let gw;
let gh;
cc.translate( win.gx, win.gy );
cc.font = fontSize + "px ''";
// ウィンドウ背景
cc.fillStyle = "white";
cc.fillRect( -padding * 2 , -padding * 2 , win.gw + padding * 4, win.gh + padding * 4 );
// ウィンドウ枠
cc.save();
cc.strokeStyle = "black";
cc.lineJoin = "bevel";
cc.lineWidth = borderWidth;
cc.strokeRect( -padding , -padding , win.gw + padding * 2, win.gh + padding * 2 );
cc.restore();
// タイトル
if( win.title ) {
cc.fillStyle = "green";
cc.fillText( win.title, 0, fontSize - 4 );
cc.translate( 0, fontSize );
}
// 項目
for( let i = 0; i < win.items.length; i++ ) {
if( i == win.cursorY ) {
//カーソル位置
cc.fillStyle = "black";
cc.fillRect( 0, i * fontSize, win.gw, fontSize );
cc.fillStyle = "white";
cc.fillText( win.items[ i ].name, 0, i * fontSize + fontSize - 4 );
} else {
//カーソル位置 以外
cc.fillStyle = "black";
cc.fillText( win.items[ i ].name, 0, i * fontSize + fontSize - 4 );
}
}
cc.restore();
} // for
} // draw()
</script>
<style>
</style>
</head>
<body onload="onloadx()">
<canvas id="test" style="
border : solid 1px black;
">
</canvas><BR>
↑↓でカーソル移動<BR>
Enter キーで決定<BR>
ESC キーでキャンセル<BR>
<BR>
テストとしては、どうぐ>やくそう>つかう とします。<BR>
これは戦闘時のメニューなので、メニューの決定時は<BR>
そのコマンドを実行するのではなく、そのコマンドを選んだ、という情報をシステムに伝えたいんです。<BR>
この、「実行」と「選択」を、1つのプログラムで兼用したいんですが、それを今悩んでいます。
</body>
</html>
上記プログラムを 実行
(訪問者のどんなニーズと この記事がつながるか)